
// ===============================================================================================================
// -*- C++ -*-
//
// MemoryBuffer.cpp - MemoryBuffer class implementation.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#include <MemoryBuffer.hpp>
#include <Common.hpp>
#include <string.h>

// Note: I use MemAlloc() and MemFree() instead of new[] and delete[] to manage memory inside the MemoryBuffer.
// This is because I don't care about exceptions, i just check the functions return.

MemoryBuffer * MemoryBuffer::Create(size_t bufferSizeInBytes)
{
	try {
		MemoryBuffer * buf = new MemoryBuffer(bufferSizeInBytes);

		if (buf->Fail())
		{
			buf->Release();
			buf = 0;
		}

		return (buf);
	}
	catch (...) {
		return (0); // Return a null pointer on error.
	}
}

MemoryBuffer * MemoryBuffer::CreateFromFileData(const std::string & fileName)
{
	MemoryBuffer * buf = 0;

	// Open the file as a binary file, for read only:
	FILE * fp = fopen(fileName.c_str(), "rb");

	if (fp != 0)
	{
		// File opened, copy its contents:

		fseek(fp, 0, SEEK_END);
		long fileSize = ftell(fp);
		fseek(fp, 0, SEEK_SET);

		if (fileSize > 0)
		{
			buf = MemoryBuffer::Create(fileSize);

			if (buf != 0)
			{
				// Buffer good, continue:
				fread(buf->GetBufferPointer(), 1, fileSize, fp);
			}
		}

		fclose(fp);
	}

	return (buf);
}

bool MemoryBuffer::ReadBytes(void * destBuf, size_t numBytesToRead)
{
	if ((destBuf == 0) || (numBytesToRead == 0))
	{
		return (false);
	}

	if (!Fail() && !EndOfStream())
	{
		size_t total;

		if ((position + numBytesToRead) > size)
		{
			total = (size - position);
		}
		else
		{
			total = numBytesToRead;
		}

		memcpy(destBuf, (reinterpret_cast<char *>(ptr) + position), total);
		position += total;

		return (true);
	}
	else
	{
		return (false);
	}
}

bool MemoryBuffer::WriteBytes(const void * srcBuf, size_t numBytesToWrite)
{
	if ((srcBuf == 0) || (numBytesToWrite == 0))
	{
		return (false);
	}

	if (!Fail() && !EndOfStream())
	{
		size_t total;

		if ((position + numBytesToWrite) > size)
		{
			total = (size - position);
		}
		else
		{
			total = numBytesToWrite;
		}

		memcpy((reinterpret_cast<char *>(ptr) + position), srcBuf, total);
		position += total;

		return (true);
	}
	else
	{
		return (false);
	}
}

bool MemoryBuffer::WriteString(const char * srcBuf)
{
	if (srcBuf != 0)
	{
		const size_t length = strlen(srcBuf);

		return (WriteBytes(srcBuf, length));
	}

	return (false);
}

bool MemoryBuffer::WriteChar(char c)
{
	if (!Fail() && !EndOfStream())
	{
		*(reinterpret_cast<char *>(ptr) + position) = c;
		++position;
		return (true);
	}

	return (false);
}

bool MemoryBuffer::ReadString(char * destBuf, size_t maxCount, char delim)
{
	if ((destBuf == 0) || (maxCount == 0))
	{
		return (false);
	}

	char * pointer = destBuf;
	char c;

	while (--maxCount)
	{
		if (!ReadChar(c))
		{
			if (pointer == destBuf)
			{
				return (false);
			}
			break;
		}

		if ((*pointer++ = c) == delim)
		{
			break;
		}
	}

	*pointer = '\0';

	return (true);
}

bool MemoryBuffer::ReadChar(char & c)
{
	if (!Fail() && !EndOfStream())
	{
		c = (*(reinterpret_cast<char *>(ptr) + position));
		++position;
		return (true);
	}

	return (false);
}

long MemoryBuffer::SeekPos(long offset, int origin)
{
	size_t newPosition = position;

	if (offset >= 0)
	{
		switch (origin)
		{
		case SEEK_SET:
			newPosition = offset;
			break;

		case SEEK_CUR:
			newPosition += offset;
			break;

		case SEEK_END:
			newPosition = (size + offset);
			break;

		default:
			newPosition = 0;
			break;
		}

		position = newPosition;
	}

	return (static_cast<long>(newPosition));
}

long MemoryBuffer::TellPos(void) const
{
	return (position);
}

size_t MemoryBuffer::GetSize(void) const
{
	return (size);
}

bool MemoryBuffer::EndOfStream(void) const
{
	return (position >= size);
}

bool MemoryBuffer::Fail(void) const
{
	return (ptr == 0);
}

void * MemoryBuffer::GetBufferPointer(void) const
{
	return (ptr);
}

MemoryBuffer * MemoryBuffer::Clone(void) const
{
	MemoryBuffer * buf = 0;

	if (!this->Fail())
	{
		buf = MemoryBuffer::Create(this->GetSize());

		if (buf != 0)
		{
			if (!buf->Fail())
			{
				// Copy over the buffer and set the read/write position:
				buf->WriteBytes(this->GetBufferPointer(), this->GetSize());
				buf->SeekPos(this->TellPos(), SEEK_SET);
			}
			else
			{
				buf->Release();
				buf = 0;
			}
		}
	}

	return (buf);
}

unsigned long MemoryBuffer::AddRef(void) const
{
	return (++refCount);
}

unsigned long MemoryBuffer::Release(void) const
{
	if (--refCount == 0)
	{
		// Time to die...
		delete this;
		return (0);
	}

	return (refCount);
}

unsigned long MemoryBuffer::ReferenceCount(void) const
{
	return (refCount);
}

MemoryBuffer::MemoryBuffer(size_t bufferSize) : ptr(0), size(0), position(0)
{
	if (bufferSize > 0)
	{
		ptr = reinterpret_cast<void *>(MemAlloc(bufferSize));

		if (ptr != 0)
		{
			// Procced if MemAlloc ok:
			size = bufferSize;
		}
	}
}

MemoryBuffer::~MemoryBuffer(void)
{
	MemFree(ptr);
}